#pragma once
#include <iostream>
#include <string>
#include <functional>

#include <cstdlib>
#include <cstring>
#include <cerrno>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"

// 归类错误码
enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    OPENDICT_ERR,
    LISTEN_ERR,
};

namespace yzc_server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0";
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    class TcpServer
    {
        // typedef void(*func_t)(int,string,uint16_t,string);
        // typedef function<void(int, string, uint16_t, string)> func_t;
        using func_t = function<void(int)>;

    public:
        TcpServer(const func_t &callback, const uint16_t &port = gport, const string &ip = defaultIp)
            : _listensockfd(-1), _ip(ip), _port(port), _callback(callback) {}

        void initServer()
        {
            // 1.创建套接字,使用tcp协议
            _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensockfd < 0)
            {
                LogMessage(FATAL, "creat socket error");
                exit(SOCKET_ERR);
            }
            LogMessage(NORMAL, "creat listensocket success:%d", _listensockfd);

            // 2.bind绑定自己的网络信息，sockfd与IP和port
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));

            local.sin_family = PF_INET; // AF_INET就是PF_INET
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensockfd, (sockaddr *)&local, sizeof(local)) < 0)
            {
                LogMessage(FATAL, "server bind error");
                exit(BIND_ERR);
            }
            LogMessage(NORMAL, "server bind success");

            // 3. tcp需要建立连接！ 设置监听状态，获取新连接
            if (listen(_listensockfd, gbacklog) == -1)
            {
                LogMessage(FATAL, "server listen error");
                exit(LISTEN_ERR);
            }
            LogMessage(NORMAL, "server listen success");
        }

        void run()
        {
            // 启动服务器
            ThreadPool<Task>::getInstance()->run();
            LogMessage(NORMAL, "Thread Pool init success");

            for (;;)
            {
                // tcp服务器不能直接发送消息
                // 4. tcpserver获取新连接
                struct sockaddr_in peer;
                socklen_t peerlen = sizeof(peer);
                memset(&peer, 0, sizeof(peer));

                // accept会接收新连接并会返回一个新的套接字
                // 最先创建的套接字用于listen和accept
                // 通过accept获取的套接字才可以用于我们的tcpserver和tcpclient通信
                int sockfd = accept(_listensockfd, (sockaddr *)(&peer), &peerlen); // 获取是sockfd用于通信
                if (sockfd < 0)
                {
                    // 这种虽然错误，但是不致命
                    LogMessage(ERROR, "server accept error, re accept");
                    continue; // 重新获取新连接
                }
                LogMessage(NORMAL, "server accept a new link success,get new sock:%d", sockfd);

                // 5.开始通信，注意tcp协议是面向字节流的。后续都是文件操作
                // 即，我们可以使用read,write等接口来读写
                //_callback(sockfd);
                // 关闭已经使用完了的套接字，如果不关闭，由于文件描述符有上限，不可滥用文件描述符
                // 否则会导致文件描述符泄漏
                // close(sockfd);

                // 线程池版
                ThreadPool<Task>::getInstance()->push(Task(sockfd, _callback));
            }
        }

    private:
        int _listensockfd; // 套接字，不是用于通信的，是用来监听和获取连接
        string _ip;
        uint16_t _port;
        func_t _callback; // 回调方法
    };
}